package com.sti.framework.models.UploadFiles.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.compress.CompressUtil;
import cn.hutool.extra.compress.extractor.Extractor;
import com.sti.framework.core.web.ResponseBodyEntity;
import com.sti.framework.models.Basics.data.MultipartFileParam;
import com.sti.framework.models.Basics.util.ChunkService;
import com.sti.framework.models.UploadFiles.data.UploadFileObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import net.lingala.zip4j.ZipFile;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Base64.Decoder;

/**
 * 文件上传
 * 需要修改application.yml中fileUploadPath
 */
@Api(tags = "文件上传")
@RestController
@RequestMapping("/Api")
public class UploadFileResumeController {

    @Value("${spring.fileUploadPath}")
    private String filePath;
    @Value("${uEditor.webUrl}")
    private String webUrl;
    @Resource
    private ChunkService chunkService;

    @ApiOperation(value = "文件上传Base64", notes = "文件上传Base64")
    @PostMapping("/UploadFileBase64")
    public ResponseBodyEntity<UploadFileObject> UploadFileBase64(@RequestBody String base64str) {

        String suffixName = "";
        if (base64str.indexOf("data:image/png;") != -1) {
            base64str = base64str.replace("data:image/png;base64,", "");
            suffixName = ".png";
        } else if (base64str.indexOf("data:image/jpeg;") != -1) {
            base64str = base64str.replace("data:image/jpeg;base64,", "");
            suffixName = ".jpg";
        } else {
            return ResponseBodyEntity.fail(ResponseBodyEntity.ResponseStatus.FAIL, "只能上传png或jpg文件");
        }
        String fileName = UUID.randomUUID() + suffixName; // 新文件名
        Boolean bl = GenerateImage(base64str, filePath + "/" + fileName);

        if (bl) {
            UploadFileObject uploadFileObject = new UploadFileObject();
            uploadFileObject.setNewName(fileName);
            uploadFileObject.setUrl("/upload/" + fileName);
            uploadFileObject.setName("");
            return ResponseBodyEntity.success(uploadFileObject);
        } else {
            return ResponseBodyEntity.fail(ResponseBodyEntity.ResponseStatus.FAIL, "上传失败");
        }
    }

    @ApiOperation(value = "文件上传", notes = "文件上传")
    @PostMapping(value = "/UploadFile")
    public ResponseBodyEntity<UploadFileObject> fileUpload(@RequestParam(value = "file") MultipartFile file) {
        if (file.isEmpty()) {
            System.out.println("文件为空");
        }
        String fileName = file.getOriginalFilename();  // 文件名
        String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);  // 后缀名

        fileName = UUID.randomUUID() + "." + suffixName; // 新文件名

        File dest = new File(filePath + "/" + fileName);
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        try {
            file.transferTo(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        UploadFileObject uploadFileObject = new UploadFileObject();
        uploadFileObject.setNewName(fileName);
        uploadFileObject.setUrl("/upload/" + fileName);
        uploadFileObject.setExt(suffixName);
        uploadFileObject.setSize(file.getSize());
        uploadFileObject.setName(file.getOriginalFilename());
        return ResponseBodyEntity.success(uploadFileObject);
    }

    @ApiOperation(value = "断点续传", notes = "需要前端配合对文件切片分次上传")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "chunkNumber", value = "当前切片数量", required = true, paramType = "query", dataTypeClass = Long.class, example = "100"),
            @ApiImplicitParam(name = "totalChunks", value = "切片总数", required = true, paramType = "query", dataTypeClass = Long.class, example = "30"),
            @ApiImplicitParam(name = "size", value = "切片大小", required = true, paramType = "query", dataTypeClass = Long.class, example = "123456"),
    })
    @PostMapping(value = "/UploadFileResume/Save")
    public ResponseBodyEntity fileUpload(@RequestParam(value = "file") MultipartFile file, @RequestParam Map<String, String> parame) {
        Map<String, Object> map = new HashMap<>();
        if (file.isEmpty()) {
            System.out.println("文件为空");
        }

        String chunkNumber = parame.get("chunkNumber");
        String totalChunks = parame.get("totalChunks");
        String size = parame.get("size");

        String fileName = file.getOriginalFilename();
        String name = fileName.substring(0, fileName.lastIndexOf("."));  // 文件名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));  // 后缀名

        File dest = new File(filePath + "/" + name + "_" + chunkNumber + suffixName);

        // 如果切片文件已经存在
        if (dest.exists()) {
            if (dest.length() == Integer.parseInt(size)) {
                map.put("over", false);
            }
        } else {
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                file.transferTo(dest.getAbsoluteFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (chunkNumber.equals(totalChunks)) {

            // 合并文件
            String[] fpaths = new String[Integer.parseInt(totalChunks)];
            for (int j = 0; j < Integer.parseInt(totalChunks); j++) {
                fpaths[j] = filePath + "/" + name + "_" + (j + 1) + suffixName;
            }
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");

            String newFileName = name + "_" + df.format(new Date()) + suffixName;
            String resultPath = filePath + "/" + newFileName;

            mergeFiles(fpaths, resultPath);

            map.put("url", "/upload/" + newFileName);
            map.put("name", file.getOriginalFilename());
            map.put("over", true);

        } else {
            map.put("over", false);
        }
        return ResponseBodyEntity.success(map);
    }

    public static boolean mergeFiles(String[] fpaths, String resultPath) {
        if (fpaths == null || fpaths.length < 1) {
            return false;
        }

        if (fpaths.length == 1) {
            return new File(fpaths[0]).renameTo(new File(resultPath));
        }

        File[] files = new File[fpaths.length];
        for (int i = 0; i < fpaths.length; i++) {
            files[i] = new File(fpaths[i]);
            if (!files[i].exists() || !files[i].isFile()) {
                return false;
            }
        }

        File resultFile = new File(resultPath);

        try {
            FileChannel resultFileChannel = new FileOutputStream(resultFile, true).getChannel();
            for (int i = 0; i < fpaths.length; i++) {
                FileChannel blk = new FileInputStream(files[i]).getChannel();
                resultFileChannel.transferFrom(blk, resultFileChannel.size(), blk.size());
                blk.close();
            }
            resultFileChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        for (int i = 0; i < fpaths.length; i++) {
            files[i].delete();
        }

        return true;
    }

    // 对字节数组字符串进行Base64解码并生成图片
    public static boolean GenerateImage(String base64Str, String filePath) {
        if (base64Str == null) // 图像数据为空
            return false;

        Decoder decoder = Base64.getDecoder();
        try {
            // Base64解码
            byte[] bytes = decoder.decode(base64Str);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            // 生成jpeg图片
            OutputStream out = new FileOutputStream(filePath);
            out.write(bytes);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @ApiOperation(value = "文件上传", notes = "文件上传")
    @PostMapping(value = "/UploadFileZip")
    public ResponseBodyEntity<UploadFileObject> fileUploadZip(@RequestParam(value = "file") MultipartFile file) {
        if (file.isEmpty()) {
            System.out.println("文件为空");
        }
        String fileName = String.valueOf(UUID.randomUUID()); // 新文件名

        File dest = new File(filePath + "/zipFile/" + fileName);
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        try {
            Extractor extractor = CompressUtil.createExtractor(CharsetUtil.defaultCharset(), file.getInputStream());
            extractor.extract(FileUtil.file(dest));
        } catch (IOException e) {
            e.printStackTrace();
        }
        UploadFileObject uploadFileObject = new UploadFileObject();
        uploadFileObject.setNewName(fileName);
        uploadFileObject.setUrl("/upload/zipFile/" + fileName);
        uploadFileObject.setSize(file.getSize());
        uploadFileObject.setName(file.getOriginalFilename());
        return ResponseBodyEntity.success(uploadFileObject);
    }

    @RequestMapping("/ChunkUpload")
    public ResponseBodyEntity chunkUpload(MultipartFileParam param, HttpServletRequest request, HttpServletResponse response) {
        File file = new File(filePath + "/chunk_box");//存储路径
        String path = file.getAbsolutePath();
        response.setContentType("text/html;charset=UTF-8");
        try {
            //判断前端Form表单格式是否支持文件上传
            boolean isMultipart = ServletFileUpload.isMultipartContent(request);
            if (!isMultipart) {
                return ResponseBodyEntity.fail(ResponseBodyEntity.ResponseStatus.FAIL, "表单格式错误");
            } else {
                param.setTaskId(param.getIdentifier());
                String zipUrl = chunkService.chunkUploadByMappedByteBuffer(param, path);
                if (StringUtils.isNotEmpty(zipUrl)) {
                    String fileName = String.valueOf(UUID.randomUUID()); // 新文件名
                    unPackZip(filePath.replace("/upload", "") + zipUrl, filePath + "/" + fileName);
                    return ResponseBodyEntity.success("/upload/" + fileName);
                } else {
                    return ResponseBodyEntity.success();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseBodyEntity.fail(ResponseBodyEntity.ResponseStatus.FAIL, "上传失败");
        }
    }

    @ApiOperation(value = "文件上传", notes = "文件上传")
    @RequestMapping("/UploadFileConfig")
    public String index(String action, String callback, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (action.equals("config")) {
            ClassPathResource classPathResource = new ClassPathResource("config.json");
            InputStream inputStream = classPathResource.getInputStream();
            return IoUtil.readUtf8(inputStream);
        } else if (action.equals("uploadimage")) {
            request.getRequestDispatcher("/Api/UploadFileGet").forward(request, response);
        } else if (action.equals("uploadvideo")) {
            request.getRequestDispatcher("/Api/UploadFileGet").forward(request, response);
        }else if (action.equals("uploadfile")) {
            request.getRequestDispatcher("/Api/UploadFileGet").forward(request, response);
        } else if (action.equals("catchimage")) {
            request.getRequestDispatcher("/Api/CatchImage").forward(request, response);
        }
        return "";
    }

    @ApiOperation(value = "文件上传", notes = "文件上传")
    @RequestMapping("/UploadFileGet")
    public Map<String, String> uploadFileGet(MultipartFile upfile) {
        Map<String, String> map = new HashMap<>();
        if (upfile.isEmpty()) {
            map.put("state", "ERROR");
        } else {
            // 上传文件
            String fileName = upfile.getOriginalFilename();  // 文件名
            String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);  // 后缀名
            fileName = UUID.randomUUID() + "." + suffixName; // 新文件名
            File dest = new File(filePath + "/" + fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                upfile.transferTo(dest);
            } catch (IOException e) {
                e.printStackTrace();
            }
            map.put("state", "SUCCESS");
            map.put("url", webUrl + "/upload/" + fileName);
            map.put("title", upfile.getOriginalFilename());
            map.put("original", upfile.getOriginalFilename());
        }
        return map;
//        if(file != null){
//            if (file.isEmpty()) {
//                System.out.println("文件为空");
//            }
//            String fileName = file.getOriginalFilename();  // 文件名
//            String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);  // 后缀名
//
//            fileName = UUID.randomUUID() + "." + suffixName; // 新文件名
//
//            File dest = new File(filePath + "/" + fileName);
//            if (!dest.getParentFile().exists()) {
//                dest.getParentFile().mkdirs();
//            }
//            try {
//                file.transferTo(dest);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//            UploadFileObject uploadFileObject = new UploadFileObject();
//            uploadFileObject.setNewName(fileName);
//            uploadFileObject.setUrl("/upload/" + fileName);
//            uploadFileObject.setExt(suffixName);
//            uploadFileObject.setSize(file.getSize());
//            uploadFileObject.setName(file.getOriginalFilename());
//            return ResponseBodyEntity.success(uploadFileObject);
//        }
    }

    @ApiOperation(value = "文件上传", notes = "文件上传")
    @RequestMapping("/CatchImage")
    public Map<String, Object> catchImage(HttpServletRequest request) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        List<Map<String, Object>> resultList = new ArrayList<>();
        String[] parameterValues = request.getParameterValues("source[]");
        if (parameterValues != null) {
            for (String sourceUrl : parameterValues) {
                URL url = new URL(sourceUrl);
                URLConnection con = url.openConnection();
                InputStream is = con.getInputStream();
                int len = 0;
                byte[] test = new byte[1024];
                // 取出后缀名
                String fileName = UUID.randomUUID() + ".png"; // 新文件名
                File dest = new File(filePath + "/" + fileName);
                if (!dest.getParentFile().exists()) {
                    dest.getParentFile().mkdirs();
                }
                try {
                    //输出流，图片输出的目的文件
                    BufferedOutputStream fos = new BufferedOutputStream(Files.newOutputStream(Paths.get(filePath + "/" + fileName)));
                    //以流的方式上传
                    while ((len = is.read(test)) != -1) {
                        fos.write(test, 0, len);
                    }
                    is.close();
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //上传文件
                Map<String, Object> map = new HashMap<>();
                map.put("source", sourceUrl);
                map.put("url", webUrl + "/upload/" + fileName);
                map.put("state", "SUCCESS");
                resultList.add(map);
            }
        }
        resultMap.put("list", resultList);
        resultMap.put("state", "SUCCESS");
        return resultMap;
    }

    public static void unPackZip(String zipFilePath, String destPath) {
        try {
            ZipFile zip = new ZipFile(new File(zipFilePath));
            zip.extractAll(destPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


